# 导入必要的库
import tkinter as tk  # 用于创建GUI界面
from PIL import Image, ImageTk  # 用于图像处理和在Tkinter中显示图像
from tkinter import filedialog  # 用于打开文件对话框
import cv2  # OpenCV库，用于计算机视觉处理
import numpy as np  # 用于数值计算和数组操作
import matplotlib.pyplot as plt  # 用于绘制图表
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg  # 用于在Tkinter中嵌入Matplotlib图表

# 定义加载图像的函数
def load_image():
    global img  # 声明使用全局变量img
    # 打开文件对话框让用户选择图像文件，限制文件类型为jpg/jpeg/png
    filepath = filedialog.askopenfilename(filetypes=[("Image files", "*.jpg *.jpeg *.png")])
    if not filepath:  # 如果用户没有选择文件就取消
        return
    img = Image.open(filepath)  # 使用PIL打开图像
    max_size = 400  # 设置图像显示的最大尺寸
    # 如果图像尺寸大于最大尺寸，则按比例缩小
    if img.size[0] > max_size or img.size[1] > max_size:
        img.thumbnail((max_size, max_size))  # 保持宽高比缩小图像
    update_image()  # 更新显示图像

# 定义更新图像显示的函数
def update_image():
    global img, img_tk, canvas  # 声明使用全局变量
    if img is None:  # 如果没有加载图像，直接返回
        return

    # ========== 亮度调整部分 ==========
    # 将PIL图像转换为numpy数组，并转换为浮点类型以便计算
    img_np = np.array(img).astype(float)
    scale = brightness_scale.get()  # 获取亮度滑块的当前值
    img_np = img_np * scale  # 调整亮度：将每个像素值乘以缩放因子
    # 将像素值限制在0-255范围内，防止溢出
    img_np = np.clip(img_np, 0, 255)
    # 将处理后的numpy数组转换回PIL图像对象，并转换为8位无符号整数格式
    img_bright = Image.fromarray(img_np.astype(np.uint8))

    # ========== 对比度调整部分 ==========
    # 将亮度调整后的图像转换为OpenCV格式的灰度图像
    img_bright_np = cv2.cvtColor(np.array(img_bright), cv2.COLOR_RGB2GRAY)
    limit = contrast_scale.get()  # 获取对比度滑块的当前值
    # 创建CLAHE(对比度受限的自适应直方图均衡化)对象
    # clipLimit参数控制对比度限制，tileGridSize定义局部区域大小
    clahe = cv2.createCLAHE(clipLimit=limit, tileGridSize=(8, 8))
    # 应用CLAHE算法增强图像对比度
    img_clahe_np = clahe.apply(img_bright_np)
    # 将处理后的numpy数组转换回PIL图像对象
    img_clahe_pil = Image.fromarray(img_clahe_np)

    # ========== 更新图像显示 ==========
    # 创建Tkinter兼容的图像对象
    img_tk = ImageTk.PhotoImage(img_clahe_pil)
    # 更新标签中的图像
    image_label.config(image=img_tk)

    # ========== 更新直方图显示 ==========
    fig.clear()  # 清除之前的图形
    # 绘制直方图：
    # ravel()将二维数组展平为一维，bins=256表示分成256个柱状，range=[0,256]表示像素值范围
    plt.hist(img_clahe_np.ravel(), bins=256, range=[0, 256])
    plt.xlabel('Pixel range: 0~255')  # 设置x轴标签
    plt.ylabel('Pixel count')  # 设置y轴标签
    canvas.draw()  # 重绘画布

# ========== 主程序开始 ==========
# 初始化全局变量
img = None  # 存储原始图像
img_tk = None  # 存储Tkinter可显示的图像对象
canvas = None  # 存储Matplotlib画布对象

# 创建主窗口
root = tk.Tk()
root.title("图像处理工具")  # 设置窗口标题

# 创建"加载图像"按钮
load_button = tk.Button(root, text="Load Image", command=load_image)
load_button.pack()  # 将按钮添加到窗口

# 创建用于显示图像的标签
image_label = tk.Label(root)
image_label.pack()

# 创建亮度调节滑块
brightness_scale = tk.Scale(
    root,  # 父窗口
    from_=0.1,  # 最小值
    to=3.0,  # 最大值
    resolution=0.1,  # 步长
    label="亮度",  # 标签文本
    orient="horizontal",  # 水平方向
    command=lambda x: update_image()  # 值改变时调用的函数
)
brightness_scale.set(1.0)  # 设置默认值
brightness_scale.pack()  # 将滑块添加到窗口

# 创建对比度调节滑块
contrast_scale = tk.Scale(
    root, 
    from_=1.0, 
    to=10.0, 
    resolution=1.0, 
    label="CLAHE clipLimit", 
    orient="horizontal", 
    command=lambda x: update_image()
)
contrast_scale.set(1.0)  # 设置默认值
contrast_scale.pack()

# 创建直方图显示区域
fig = plt.figure(figsize=(4, 2))  # 创建图形对象，设置大小
# 创建可以在Tkinter中显示的Matplotlib画布
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack()  # 将画布添加到窗口

# 启动主事件循环
root.mainloop()